<template>
  <div class="video-upload">
    <a-form
      ref="formRef"
      :model="form"
      :rules="rules"
      :label-col="{ span: 4 }"
    >
      <div
        class="upload-dragger"
        @dragover.prevent="handleDragOver"
        @dragleave="handleDragLeave"
        @drop.prevent="handleDrop"
        @click="triggerFileInput"
        :class="{ 'drag-over': isDragOver }"
        v-if="fileStatus === fileStatusOptions.INIT || fileStatus === fileStatusOptions.ERROR"
      >
        <div v-if="fileList.length === 0">
          <p class="upload-drag-icon">
            <UploadOutlined />
          </p>
          <p class="upload-text">点击或拖拽上传</p>
          <p class="upload-hint">
            注意：仅支持上传 mp4、flv、mkv、mov、avi、wmv 和 webm 格式的视频，且文件大小不超过 10G
          </p>
          <input
            type="file"
            @change="handleChange"
            style="display: none"
            ref="fileInput"
          />
          <button
            v-show="false"
            type="button"
            class="upload-button"
            >选择文件</button
          >
        </div>
        <div v-else>
          <p class="upload-text">已选择文件: {{ fileList[0].name }}</p>
        </div>
      </div>

      <div
        class="calculate-file-hash"
        v-else-if="fileStatus === fileStatusOptions.CALC"
      >
        <div class="loading-container">
          <div class="loading-spinner"></div>
          <p>正在计算文件哈希中...</p>
        </div>
      </div>

      <div
        class="upload-progress"
        v-else
      >
        <a-progress :percent="uploadProgress" />
        <div class="upload-progress-actions">
          <div
            class="upload-continue"
            title="继续"
            v-if="fileStatus === fileStatusOptions.STOP"
            @click="handleUploadContinue"
          >
            <PlayCircleOutlined />
          </div>

          <div
            class="upload-pause"
            title="暂停"
            v-if="fileStatus === fileStatusOptions.UPLOADING"
            @click="handleUploadPause"
          >
            <PauseCircleOutlined />
          </div>

          <div
            class="upload-delete"
            title="删除"
            v-if="fileStatus !== fileStatusOptions.INIT && fileStatus !== fileStatusOptions.ERROR"
            @click="handleUploadDelete"
          >
            <DeleteOutlined />
          </div>
        </div>
      </div>

      <a-form-item
        label="视频封面"
        name="thumbnailUrl"
      >
        <a-upload
          list-type="picture-card"
          class="upload-thumbnail"
          :multiple="false"
          :show-upload-list="false"
          :before-upload="handleBeforeThumbnailUpload"
          :file-list="thumbnailList"
          :disabled="
            fileStatus === fileStatusOptions.INIT || fileStatus === fileStatusOptions.ERROR
          "
          :max-count="1"
          @change="handleThumbnailUpload"
        >
          <div v-if="!form.thumbnailUrl">
            <p class="upload-thumbnail-icon">
              <UploadOutlined />
            </p>
          </div>

          <template v-else>
            <img
              :src="form.thumbnailUrl"
              alt="视频封面"
              style="width: 100%; height: 100%"
            />
          </template>
        </a-upload>
      </a-form-item>

      <a-form-item
        label="视频标题"
        name="title"
      >
        <a-input
          v-model:value="form.title"
          placeholder="请输入视频标题"
          maxlength="25"
          show-count
        />
      </a-form-item>

      <a-form-item
        label="视频简介"
        name="description"
      >
        <a-textarea
          v-model:value="form.description"
          placeholder="请输入视频简介"
          :auto-size="{ minRows: 3, maxRows: 5 }"
          maxlength="500"
          show-count
        />
      </a-form-item>

      <a-form-item
        label="视频类型"
        name="type"
      >
        <a-select
          v-model:value="form.type"
          placeholder="请选择视频类型"
          :options="videoTypeOptions"
        />
      </a-form-item>

      <a-form-item
        label="视频标签"
        name="labels"
      >
        <a-select
          mode="tags"
          placeholder="请输入视频标签(最多不超过 10 个)"
          :max-tag-count="5"
          :max-tag-text-length="10"
          allow-clear
          :options="[]"
          v-model:value="form.labels"
          @change="handleTagsChange"
        />
      </a-form-item>

      <!-- 滑块 -->
      <a-form-item
        label="视频可见性"
        name="isVisible"
        required
      >
        <a-switch
          v-model:checked="form.isVisible"
          un-checked-children="隐藏"
          :un-checked-value="0"
          checked-children="可见"
          :checked-value="1"
        />
      </a-form-item>
    </a-form>

    <div class="submit-btn-container">
      <a-button
        type="primary"
        class="submit-btn"
        @click="handleSubmit"
      >
        提 交
      </a-button>
    </div>
  </div>
</template>

<script setup lang="ts">
import {
  DeleteOutlined,
  PauseCircleOutlined,
  PlayCircleOutlined,
  UploadOutlined
} from '@ant-design/icons-vue'
import { message, Modal, UploadChangeParam, UploadProps } from 'ant-design-vue'
import { checkFileExist } from '@/api/file'
import { Upload } from 'tus-js-client'
import { getAccessToken } from '@/utils/auth'
import { sha256 } from 'js-sha256'
import { getDictList } from '@/api/dict'
import { CvVideoSaveReqVO } from '@/types/video'
import { useUserStore } from '@/store/modules/user'
import { saveVideo } from '@/api/video'

const userStore = useUserStore()

const videoTypeOptions = ref<any>([])
onMounted(async () => {
  videoTypeOptions.value = await getDictList('cv_video_types')
})

const formRef = ref()
const form = reactive({
  id: '',
  title: '',
  description: '',
  type: '',
  url: '',
  thumbnail: '',
  thumbnailUrl: '',
  labels: [],
  isVisible: 1
})

const rules = {
  thumbnailUrl: [
    {
      required: true,
      message: '请上传视频封面',
      trigger: 'change'
    }
  ],
  title: [
    {
      required: true,
      message: '请输入视频标题',
      trigger: 'blur'
    }
  ],
  type: [
    {
      required: true,
      message: '请选择视频类型',
      trigger: 'change'
    }
  ]
}

/**
 * 标签变更
 */
const handleTagsChange = (tags: string[]) => {
  // 文本长度
  const maxLength = 20
  // 限制标签的数量
  const maxTags = 10

  // 限制每个标签的长度
  if (tags.some(tag => tag.length > maxLength)) {
    message.error('标签长度不能超过 10 个字符')
    form.labels = tags.map(tag => tag.slice(0, maxLength))
  }

  if (tags.length > maxTags) {
    message.error('标签数量超出限制')
    form.labels = tags.slice(0, maxTags)
  }
}

/**
 * 关联合集
 */
const handleCollectVideo = () => {}

/**
 * 提交表单
 */
const handleSubmit = () => {
  if (!formRef.value) {
    return
  }

  formRef.value
    .validate()
    .then(async () => {
      if (
        fileStatus.value === fileStatusOptions.INIT ||
        fileStatus.value === fileStatusOptions.ERROR ||
        !form.url
      ) {
        message.error('请选择视频文件')
        return
      }

      if (!form.thumbnail || !form.thumbnailUrl) {
        message.error('请选择视频封面')
        return
      }

      if (
        fileStatus.value === fileStatusOptions.UPLOADING ||
        fileStatus.value === fileStatusOptions.STOP
      ) {
        message.error('视频上传中，请稍后')
        return
      }

      if (fileStatus.value === fileStatusOptions.UPLOADED) {
        const data: CvVideoSaveReqVO = <any>{
          id: null,
          userId: userStore.getUserId,
          title: form.title,
          description: form.description,
          type: form.type,
          url: form.url,
          thumbnail: form.thumbnail,
          labelList: form.labels,
          isVisible: form.isVisible
        }
        const res = await saveVideo(data)
        if (res) {
          message.success('视频上传成功')
          form.id = ''
          form.title = ''
          form.description = ''
          form.type = ''
          form.url = ''
          form.thumbnail = ''
          form.thumbnailUrl = ''
          form.labels = []
          form.isVisible = 1
          fileStatus.value = fileStatusOptions.INIT
          fileList.value = []
          thumbnailList.value = []
          uploadProgress.value = 0
        }
      }
    })
    .catch(() => {})
}

// ==================== 视频上传 ====================

const uploadUrl = import.meta.env.VITE_UPLOAD_URL

/**
 * 文件状态
 */
const fileStatusOptions = {
  INIT: 0, // 初始化，未上传
  CALC: 1, // 计算哈希
  UPLOADING: 2, // 上传中
  STOP: 3, // 暂停
  UPLOADED: 4, // 已上传
  ERROR: 5 // 上传失败
}

const fileInput = ref(null)
const fileList = ref([])
const fileStatus = ref(fileStatusOptions.INIT)
const uploadProgress = ref(100)
const isDragOver = ref(false)

/**
 * 检测条件
 */
const allowedSuffix = [
  'video/mp4',
  'video/x-flv',
  'video/x-matroska',
  'video/quicktime',
  'video/x-msvideo',
  'video/x-ms-wmv',
  'video/webm'
]
const fileSizeLimit = 10 * 1024 * 1024 * 1024 // 10G

/**
 * 校验文件
 */
const validateFile = (file: File) => {
  if (file.size > fileSizeLimit) {
    message.error('视频大小不能超过 10G')
    return false
  }

  if (!allowedSuffix.includes(file.type)) {
    message.error('视频格式不正确')
    return false
  }

  return true
}

const handleDragOver = () => {
  isDragOver.value = true
}

const handleDragLeave = () => {
  isDragOver.value = false
}

const handleDrop = async (event: DragEvent) => {
  isDragOver.value = false
  const files = event.dataTransfer.files
  if (files.length > 0) {
    const file = files[0]
    if (await validateFile(file)) {
      fileList.value = [file]
      handleVideoUpload()
    }
  }
}

const handleChange = async (event: Event) => {
  const target = event.target as HTMLInputElement
  const files = target.files
  if (files && files.length > 0) {
    const file = files[0]
    if (await validateFile(file)) {
      fileList.value = [file]
      handleVideoUpload()
    }
  }
}

const triggerFileInput = () => {
  fileInput.value.click()
}

let uploadVideoTask = null
const handleVideoUpload = () => {
  fileStatus.value = fileStatusOptions.CALC
  uploadProgress.value = 0

  // worker.js 计算 hash
  const worker = new Worker(new URL('@/worker/worker.ts', import.meta.url), {
    type: 'module'
  })

  worker.postMessage({
    file: fileList.value[0]
  })

  worker.onmessage = async (event: any) => {
    const result = event.data
    worker.terminate()
    if (result.code === 200) {
      fileStatus.value = fileStatusOptions.UPLOADING

      // 截取视频第一帧
      const video = document.createElement('video')
      video.preload = 'metadata'
      video.src = URL.createObjectURL(fileList.value[0])
      video.onloadedmetadata = () => {
        video.currentTime = 0.1
        video.onseeked = async () => {
          const canvas = document.createElement('canvas')
          canvas.width = video.videoWidth
          canvas.height = video.videoHeight
          canvas.getContext('2d').drawImage(video, 0, 0, canvas.width, canvas.height)
          form.thumbnailUrl = canvas.toDataURL('image/jpeg')

          // 将 Data URL 转换为 File 对象
          const blob = await (await fetch(form.thumbnailUrl)).blob()
          const file = {
            name: 'thumbnail.jpg',
            originFileObj: blob
          }

          // 上传该路径
          await handleThumbnailUpload(<any>{
            file: file
          })
        }
      }

      const hash = result.data
      const res = await checkFileExist(hash as string)
      if (res?.id && res?.path) {
        form.url = res?.id
        uploadProgress.value = 100
        fileStatus.value = fileStatusOptions.UPLOADED
      } else {
        if (uploadUrl) {
          uploadVideoTask = new Upload(fileList.value[0], {
            endpoint: uploadUrl,
            metadata: {
              filename: fileList.value[0].name,
              uploadType: 'video',
              hash: hash
            },
            headers: {
              Authorization: `Bearer ${getAccessToken()}`
            },
            onError: error => {
              console.error('上传失败:', error)
              message.error('上传失败')
            },
            onProgress: (bytesUploaded, bytesTotal) => {
              const percentage = ((bytesUploaded / bytesTotal) * 100).toFixed(2)
              console.log(`上传进度: ${percentage}% (${bytesUploaded}/${bytesTotal})`)
              uploadProgress.value = parseFloat(percentage)
            },
            onSuccess: async () => {
              const { id } = await checkFileExist(hash)
              if (id) {
                form.url = id
                message.success('视频上传成功')
                fileStatus.value = fileStatusOptions.UPLOADED
              } else {
                message.error('上传失败，请重新上传')
                fileStatus.value = fileStatusOptions.ERROR
                fileList.value = []
                form.url = ''
                uploadProgress.value = 0
                form.thumbnail = ''
                form.thumbnailUrl = ''
                thumbnailList.value = []
              }
            }
          })

          await uploadVideoTask.start()
        }
      }
    } else {
      message.error('视频上传失败：', result.msg)
      fileStatus.value = fileStatusOptions.ERROR
      fileList.value = []
      form.url = ''
      uploadProgress.value = 0
      form.thumbnail = ''
      form.thumbnailUrl = ''
      thumbnailList.value = []
      return
    }
  }
}

const handleUploadPause = () => {
  if (uploadVideoTask) {
    uploadVideoTask.abort()
    fileStatus.value = fileStatusOptions.STOP
    message.success('上传已暂停')
  }
}

const handleUploadContinue = () => {
  if (uploadVideoTask) {
    uploadVideoTask.start()
    fileStatus.value = fileStatusOptions.UPLOADING
    message.success('上传继续')
  }
}

const handleUploadDelete = () => {
  Modal.confirm({
    title: '确定要删除该视频吗？',
    content: '删除后无法恢复',
    okText: '确定',
    cancelText: '取消',
    onOk() {
      fileStatus.value = fileStatusOptions.INIT
      fileList.value = []
      form.url = ''
      uploadProgress.value = 0
      form.thumbnail = ''
      form.thumbnailUrl = ''
      thumbnailList.value = []
    }
  })
}

// ==================== 视频封面上传 ====================

const thumbnailList = ref([])
const thumbnailIsUploading = ref(false)

/**
 * 视频封面上传前校验
 */
const handleBeforeThumbnailUpload = (file: UploadProps['fileList'][number]) => {
  const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png'

  if (!isJpgOrPng) {
    message.error('图片格式仅支持：jpg 和 png')
    return false
  }

  const isLt50M = file.size / 1024 / 1024 < 100
  if (!isLt50M) {
    message.error('图片大小不超过 100MB')
    return false
  }

  return isJpgOrPng && isLt50M
}

/**
 * 视频封面上传
 */
const handleThumbnailUpload = async (info: UploadChangeParam) => {
  if (thumbnailIsUploading.value) {
    message.error('正在上传视频封面，请稍后')
    return
  }

  const file = info.file
  if (!file || !file.originFileObj) {
    return
  }

  thumbnailIsUploading.value = true
  try {
    const reader = new FileReader()
    reader.readAsArrayBuffer(file.originFileObj)
    reader.onload = async () => {
      if (reader.result) {
        const uint8View = new Uint8Array(reader.result as ArrayBuffer)
        let hash = sha256.create()
        hash.update(uint8View)
        const hex = hash.hex()
        const res = await checkFileExist(hex)
        if (res?.id && res?.path) {
          form.thumbnail = res?.id
          form.thumbnailUrl = res?.path
          message.success('视频封面上传成功')
        } else {
          if (uploadUrl) {
            const upload = new Upload(file.originFileObj, {
              endpoint: uploadUrl,
              metadata: {
                filename: file.name,
                uploadType: 'thumbnail',
                hash: hex
              },
              headers: {
                Authorization: `Bearer ${getAccessToken()}`
              },
              onError: error => {
                console.error('上传失败:', error)
                message.error('上传失败')
              },
              onProgress: (bytesUploaded, bytesTotal) => {
                const percentage = ((bytesUploaded / bytesTotal) * 100).toFixed(2)
                console.log(`上传进度: ${percentage}% (${bytesUploaded}/${bytesTotal})`)
              },
              onSuccess: async () => {
                const { id, path } = await checkFileExist(hex)
                if (id && path) {
                  form.thumbnail = id
                  form.thumbnailUrl = path
                  message.success('视频封面上传成功')
                } else {
                  message.error('视频封面上传失败，请重新上传')
                  form.thumbnail = ''
                  form.thumbnailUrl = ''
                }
              }
            })

            await upload.start()
          }
        }
      }
    }
    reader.onerror = error => {
      message.error('视频封面上传失败: 计算文件哈希失败')
    }
  } catch (error) {
    console.error('视频封面上传失败:', error)
    message.error('视频封面上传失败')
    form.thumbnail = ''
    form.thumbnailUrl = ''
  } finally {
    thumbnailIsUploading.value = false
    thumbnailList.value = [] // 清空文件列表，避免重复触发 change 事件
  }
}
</script>

<style scoped>
.video-upload {
  padding: 30px;
  background-color: #fff;
}

.upload-dragger {
  border: 2px dashed #d9d9d9;
  border-radius: 4px;
  padding: 20px;
  text-align: center;
  cursor: pointer;
  transition: border-color 0.3s;
  margin-bottom: 30px;
}

.upload-dragger.drag-over {
  border-color: #1890ff;
}

.upload-drag-icon {
  font-size: 48px;
  color: #999;
  margin-bottom: 10px;
}

.upload-text {
  margin-top: 12px;
  font-size: 16px;
  color: #333;
}

.upload-hint {
  margin-top: 8px;
  font-size: 14px;
  color: #999;
}

.calculate-file-hash {
  margin-bottom: 20px;
}

.loading-container {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 20px;
  border: 1px solid #ccc;
  border-radius: 8px;
  background-color: #f9f9f9;
}

.loading-spinner {
  border: 4px solid rgba(0, 0, 0, 0.1);
  width: 36px;
  height: 36px;
  border-radius: 50%;
  border-left-color: #09f;
  animation: spin 1s linear infinite;
}

@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}

.upload-progress,
.upload-progress-actions {
  display: flex;
  gap: 10px;
  margin-bottom: 20px;
}

.upload-pause,
.upload-continue,
.upload-delete {
  cursor: pointer;
}

.upload-delete {
  color: #f5222d;
}

.upload-thumbnail {
  margin-left: 20px;
}

.submit-btn-container {
  display: flex;
  justify-content: center;
}

.submit-btn {
  margin: 24px 0;
  width: 200px;
  height: 40px;
}
</style>
